package com.zqf.shiro.utils;

import com.alibaba.fastjson.JSON;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.zqf.shiro.pojo.LoginUser;
import io.jsonwebtoken.*;
import org.apache.commons.beanutils.BeanMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.security.Key;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Hero
 * @date 7/12/2017
 */
public class JwtHelper {

    private static final Logger log = LoggerFactory.getLogger(JwtHelper.class);

    public static boolean veritySigned(String jsonWebToken, String base64Security) {
        return Jwts.parser()
                .setSigningKey(Base64.getDecoder().decode(base64Security))
                .isSigned(jsonWebToken);
    }

    public static boolean verify(String jsonWebToken, String base64Security) {
        try {
            final Claims claims = (Claims) Jwts.parser()
                    .setSigningKey(base64Security)
                    .parse(jsonWebToken)
                    .getBody();
            return true;
        } catch (ExpiredJwtException e) {
            log.debug("token过期失效");
            return false;
        } catch (MalformedJwtException e) {
            log.debug("未知错误");
            return false;
        } catch (SignatureException e) {
            log.debug("token签名失效");
            return false;
        } catch (IllegalArgumentException e) {
            log.debug("非法参数");
            return false;
        }


    }


    public static String createAccessToken(LoginUser user, String iss, String aud, long ttlMillis, String base64Security) {
        // 采用椭圆曲线加密算法, 提升加密速度
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        //生成签名密钥
        SignatureAlgorithm signatureAlgorithm = getAlgorithm();
        Key signingKey = getKey(base64Security, signatureAlgorithm.getJcaName());

        Map<String, Object> claims = user2map(user);

        //添加构成JWT的参数
        JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JWT")
                .addClaims(claims)
                .setIssuer(iss)
                .setAudience(aud)
                .setSubject(user.getUsername())
                .signWith(signatureAlgorithm, signingKey);

        //添加Token过期时间
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            builder.setExpiration(exp).setNotBefore(now);
        }

        //生成JWT
        return builder.compact();
    }

    public static String createRefreshToken(String userName, String iss, String aud, long ttlMillis, String base64Security) {

        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        //生成签名密钥
        SignatureAlgorithm signatureAlgorithm = getAlgorithm();
        Key signingKey = getKey(base64Security, signatureAlgorithm.getJcaName());

        //添加构成JWT的参数
        JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JWT")
                .claim("scope", "REFRESH")
                .setIssuer(iss)
                .setAudience(aud)
                .setSubject(userName)
                .signWith(signatureAlgorithm, signingKey);
        //添加Token过期时间
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            builder.setExpiration(exp).setNotBefore(now);
        }

        //生成JWT
        return builder.compact();
    }

    /**
     * 获取jwt 唯一身份识别码
     *
     * @return 登录用户信息
     */
    public static String createJti() {
        return String.valueOf(System.nanoTime());
    }

    public static LoginUser verifyAndGetUser(String jwt, String base64Security) {
        if (StringUtils.isBlank(jwt)) {
            log.debug("JWT校验commons：jwt为空，校验失败");
            throw new IllegalArgumentException("jwt 不能为空");
        }
        if (StringUtils.isBlank(base64Security)) {
            log.debug("JWT校验commons：base64Security为空，校验失败");
            throw new IllegalArgumentException("key 不能为空");
        }
        if (verify(jwt, base64Security)) {
            String userStr = new String(Base64.getUrlDecoder().decode(jwt.split("[.]")[1]));
            return JSON.parseObject(userStr, LoginUser.class);
        } else {
            log.debug("JWT校验commons：verify()失败，校验失败");
        }
        return null;
    }


    /**
     * 校验jwt
     */
    public static LoginUser verifySignedAndGetUser(String jwt, String base64Security) {
        if (StringUtils.isBlank(jwt)) {
            log.debug("JWT校验commons：jwt为空，校验失败");
            throw new IllegalArgumentException("jwt 不能为空");
        }
        if (StringUtils.isBlank(base64Security)) {
            log.debug("JWT校验commons：base64Security为空，校验失败");
            throw new IllegalArgumentException("key 不能为空");
        }
        if (veritySigned(jwt, base64Security)) {
            String userStr = new String(Base64.getUrlDecoder().decode(jwt.split("[.]")[1]));
            return JSON.parseObject(userStr, LoginUser.class);
        } else {
            log.debug("JWT校验commons：verify()失败，校验失败");
        }
        return null;
    }


    /**
     * 获取加密算法
     */
    private static SignatureAlgorithm getAlgorithm() {
        return SignatureAlgorithm.HS256;
    }

    private static Key getKey(String base64Security, String algorithm) {
        byte[] apiKeySecretBytes = Base64.getDecoder().decode(base64Security);
        return new SecretKeySpec(apiKeySecretBytes, algorithm);
    }

    public static LoginUser checkAndGetUser(String authorization) {
        //为空不合法
        if (StringUtils.isBlank(authorization)) {
            return null;
        }
        // 格式不合法
        if (!authorization.startsWith("Bearer ")) {
            return null;
        }
        String[] jwt = authorization.split("[.]");
        if (jwt.length < 3) {
            return null;
        }

        String payload = new String(Base64.getUrlDecoder().decode(jwt[1]));
        if (StringUtils.isEmpty(payload)) {
            return null;
        }
        return JSON.parseObject(payload, LoginUser.class);
    }


    private static Map<String, Object> user2map(LoginUser bean) {
        Map<String, Object> map = new HashMap<>(16);
        new BeanMap(bean).forEach((k, v) -> {
            if ((v instanceof Long || v instanceof BigInteger) && v != null) {
                map.put(String.valueOf(k), String.valueOf(v));
            } else {
                map.put(String.valueOf(k), v);
            }
        });
        map.remove("authenticated");
        return map;
    }


}
